package com.xframe.domain.sys;

import com.xframe.domain.BaseModel;
import com.xframe.domain.role.IUserCredentials;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.xframe.xdal.core.DataBaseAccess;
import com.xframe.xdal.core.annotation.*;
import com.xframe.xdal.core.constant.AutoFillType;
import com.xframe.xdal.core.constant.ErrorCode;
import com.xframe.xdal.core.domain.SysBaseEntity;
import com.xframe.xdal.core.domain.XDynamicModel;
import com.xframe.xdal.core.exception.DalFrameException;
import com.xframe.xdal.core.handler.IAfterConstruction;
import com.xframe.xdal.core.handler.IBeanInit;
import com.xframe.xdal.core.model.AKQueryModel;
import com.xframe.xdal.core.model.mapping.*;
import com.xframe.xdal.core.model.mapping.IColumnMapping;
import com.xframe.xdal.core.model.mapping.IRelation;
import com.xframe.xdal.core.model.mapping.ITableInfo;
import com.xframe.xdal.core.util.*;
import lombok.Data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 系统实体表
 *
 * @author xufeng
 * @version 1.0
 * @date 2023-03-28
 */
@Data
@XTable(value = "SYS_MODEL", label = "模型表")
@XSelectItem(keyName = "modelCode",codeName = "modelCode",textName = "modelName")
@XHasMany(typeName = "com.xframe.domain.sys.SysModelAttr",foreignKey = "modelId", targetKey = "modelId",as = "modelAttrList",refName = "refSysModel")
@XHasMany(typeName = "com.xframe.domain.sys.SysModelBelong",foreignKey = "modelId", targetKey = "modelId",as = "modelBelongList")
@XHasMany(typeName = "com.xframe.domain.sys.SysModelMany",foreignKey = "modelId", targetKey = "modelId",as = "modelManyList")
@XHasMany(typeName = "com.xframe.domain.sys.SysModelAttrValProc",foreignKey = "modelId", targetKey = "modelId",as = "attrValProcList")
@XHasMany(typeName = "com.xframe.domain.sys.SysModelSer",foreignKey = "modelId", targetKey = "modelId",as = "serList")
public class SysModel extends BaseModel implements ITableInfo, IBeanInit, IAfterConstruction {

    public static final String PK_NAME = "modelId";

    /**
     * 模型ID
     */
    @AutoFill(AutoFillType.Snowflake)
    @JsonSerialize(using = com.fasterxml.jackson.databind.ser.std.ToStringSerializer.class)
    @XColumn(name = "MODEL_ID", primaryKeyFlag = true,desc = "模型ID")
    private Long modelId;
    /**
     * 模型编码
     */
    @XQueryCondition
    @XGridTableRef
    @XColumn(name = "MODEL_CODE",uniqueFlag = true,desc = "模型编码",length = 50)
    private String modelCode;
    /**
     * 模型名称
     */
    @XQueryCondition
    @XColumn(name = "MODEL_NAME",desc = "模型名称",length = 50)
    private String modelName;

    /**
     * 模型别名
     */
    @XColumn(name = "MODEL_AL_NAME",desc = "模型别名",length = 50)
    private String alName;
    /**
     * 模型物理表名称
     */
    @XColumn(name = "TABLE_NAME",desc = "模型物理表名称",length = 200)
    private String tableName;

    /**
     * 索引名称
     */
    @XColumn(name = "INDEX_NAME",desc = "索引名称",length = 200)
    private String indexName;

    /**
     * 模型对象的类名称
     */
    @XColumn(name = "CLASS_NAME",desc = "类名称",length = 200)
    private String className;
    /**
     * 系统内置标签
     */
    @AutoFill(defVal = "0")
    @XColumn(name = "SYSTEM_FLAG",desc = "系统内置标签")
    private Integer systemFlag;


    /**
     * 状态 0 = 新建 1 = 发布 2 = 停用
     */
    @AutoFill(defVal = "0")
    @XColumn(name = "MODEL_STATE",desc = "状态",length = 20)
    private String modelState;


    /**
     * 模型类别  1  = 自定义创建 2 = JavaBean创建
     */
    @AutoFill(defVal = "1")
    @XColumn(name = "MODEL_TYPE",desc = "模型类别",length = 10)
    private String modelType;


    /**
     * 数据源编码
     */
    @AutoFill(defVal = "main")
    @XColumn(name = "ds_code",desc = "数据源编码",length = 10)
    private String dataSourceDefCode;

    /**
     * 模型字段列表
     */
    private List<SysModelAttr> modelAttrList;
    /**
     * 模型引用关系列表
     */
    private List<SysModelBelong> modelBelongList;
    /**
     * 模型引用子表关系列表
     */
    private List<SysModelMany> modelManyList;

    /**
     * 引用的字段处理器
     */
    private List<SysModelAttrValProc> attrValProcList;

    /**
     * 引用的序列化处理类
     */
    private List<SysModelSer> serList;

    /**
     * 引用的存储库
     */
    //@JsonIgnore
    //private SysModelRepository refRepository;
    @JsonIgnore
    public Class<?> getModelDataType() {
        if(StringUtil.isEmpty(this.className)){
            return XDynamicModel.class;
        }else {
            try{
                return Class.forName(this.className);
            }catch (ClassNotFoundException e) {
                return XDynamicModel.class;
            }
        }
    }


    public void init(){
        if(this.modelAttrList != null && this.modelAttrList.size() > 0) {
            Collections.sort(this.modelAttrList);
            if(attrValProcList != null) {
                for (SysModelAttrValProc sysModelAttrValProc : attrValProcList) {
                    findSysModelAttrValProc(sysModelAttrValProc);
                }
            }
        }
    }

    private void findSysModelAttrValProc(SysModelAttrValProc sysModelAttrValProc){
        for(SysModelAttr sysModelAttr : this.modelAttrList){
            if(sysModelAttr.getAttrId().equals(sysModelAttrValProc.getAttrId())){
                if(sysModelAttr.getAttrValProcList() == null){
                    sysModelAttr.setAttrValProcList(new ArrayList<>());
                }
                sysModelAttrValProc.setRefSysModelAttr(ModelUtil.cloneByTableInfo(sysModelAttr));
                sysModelAttr.getAttrValProcList().add(sysModelAttrValProc);
                break;
            }
        }
    }

    public void setIndexName(String indexName){
        if(indexName != null){
            this.indexName = indexName.toLowerCase();
        }else {
            this.indexName = null;
        }
    }
    @Override
    public String getIndexName() {
        if(StringUtil.isEmpty(this.indexName)){
            this.indexName = this.getAsName().toLowerCase();
        }
        return this.indexName;
    }


    @Override
    public String getName() {
        return this.tableName;
    }

    @Override
    public String getCode() {
        return this.modelCode;
    }

    @Override
    public String getAsName() {
        return this.alName;
    }

    @Override
    public String getLabel() {
        return this.modelName;
    }

    @JsonIgnore
    @Override
    public List<IColumnMapping> getColumnMappingList() {
        return new ArrayList<>(modelAttrList);
    }

    @Override
    public void setColumnMappingList(List<IColumnMapping> columnMappingList) {

    }

    @JsonIgnore
    @Override
    public List<IRelation> getBelongsToMappingList() {
        if(modelBelongList == null){
            modelBelongList = new ArrayList<>();
        }
        return new ArrayList<>(modelBelongList);
    }

    @JsonIgnore
    @Override
    public List<IRelation> getHasManyMappingList() {
        if(modelManyList == null){
            modelManyList = new ArrayList<>();
        }
        return new ArrayList<>(modelManyList);
    }
    /**
     * 查找一个IColumnMapping
     *
     * @param fieldName 属性名称
     * @return IColumnMapping
     */
    @Override
    public IColumnMapping findOneByFieldName(String fieldName) {
        IColumnMapping columnMapping = null;
        for (int index = 0; modelAttrList != null && index < modelAttrList.size(); index++) {
            if (modelAttrList.get(index).getFieldName().equals(fieldName)) {
                columnMapping = modelAttrList.get(index);
                break;
            }
        }
        return columnMapping;
    }
    /**
     * 查找一个IColumnMapping
     *
     * @param name 字段名称
     * @return IColumnMapping
     */
    @Override
    public IColumnMapping findOneByName(String name) {
        IColumnMapping columnMapping = null;
        for (int index = 0; modelAttrList != null && index < modelAttrList.size(); index++) {
            if (modelAttrList.get(index).getName().equals(name)) {
                columnMapping = modelAttrList.get(index);
                break;
            }
        }
        return columnMapping;
    }
    /**
     * 获取第一个数据表 主键列映射
     *
     * @return IColumnMapping
     */
    @Override
    public IColumnMapping findPkFirst() {
        IColumnMapping columnMapping = null;
        for (int index = 0; modelAttrList != null && index < modelAttrList.size(); index++) {
            if (modelAttrList.get(index).getPrimaryKeyFlag()) {
                columnMapping = modelAttrList.get(index);
                break;
            }
        }
        return columnMapping;
    }

    /**
     * 获取第一个数据表 版本号列映射
     *
     * @return IColumnMapping
     */
    @Override
    public IColumnMapping findVer() {
        IColumnMapping columnMapping = null;
        for (int index = 0; modelAttrList != null && index < modelAttrList.size(); index++) {
            if (modelAttrList.get(index).getVersionFlag()) {
                columnMapping = modelAttrList.get(index);
                break;
            }
        }
        return columnMapping;
    }

    /**
     * 获取所有数据表 不重复值列映射集合
     *
     * @return List<IColumnMapping>
     */
    @Override
    public List<IColumnMapping> findUniqueKeyList() {
        List<IColumnMapping> findList = new ArrayList<>();
        for (int index = 0; modelAttrList != null && index < modelAttrList.size(); index++) {
            // 取不同的判断条件
            if (modelAttrList.get(index).getUniqueFlag()) {
                findList.add(modelAttrList.get(index));
            }

        }
        return findList;
    }


    /**
     * 获取数据表所有可以更新的列映射集合
     *
     * @return List<IColumnMapping>
     */
    @Override
    public List<IColumnMapping> findCanUpdateList() {
        List<IColumnMapping> findList = new ArrayList<>();
        for (int index = 0; modelAttrList != null && index < modelAttrList.size(); index++) {
            IColumnMapping columnMapping = modelAttrList.get(index);
            if (!columnMapping.getPrimaryKeyFlag() && !columnMapping.getUniqueFlag() && !columnMapping.getReadonly() && !columnMapping.getVersionFlag()) {
                findList.add(modelAttrList.get(index));
            }
        }
        return findList;
    }

    @Override
    public String findPkField(){
        IColumnMapping columnMapping = findPkFirst();
        if(columnMapping == null){
            return null;
        }
        return ((SysModelAttr)columnMapping).getAttrName();
    }


    /**
     * @param domainType 类型 完整包名  class
     * @param desc  文本
     * @return
     */
    public static SysModel valueOf(String domainType,String desc,IUserCredentials userCredentials) throws DalFrameException {
        SysModel sysModel = new SysModel();
        sysModel.setModelId(IdCreateUtil.getSnowflakeId());
        // 模型引用子表关系
        List<SysModelMany> modelManyList = new ArrayList<>();
        // 模型引用关系
        List<SysModelBelong> sysModelBelongList = new ArrayList<>();
        //  模型字段列表
        List<SysModelAttr> modelAttrList = new ArrayList<>();
        // 引用的字段处理器
        List<SysModelAttrValProc> sysModelAttrValProcList = new ArrayList<>();
        try {
            Class<?> dataType = Class.forName(domainType);
            if(dataType == null){
                throw new DalFrameException(ErrorCode.ARGS_ERROR,"数据类型不能为空");
            }
            ITableInfo tableInfo = TableInfoBuilder.BuildTableInfoByAnnotation(dataType);

            if(tableInfo == null){
                throw new DalFrameException(ErrorCode.ARGS_ERROR,"表信息不能为空");
            }
            sysModel.setModelName(desc);
            sysModel.setModelCode(dataType.getName());
            sysModel.setAlName(tableInfo.getAsName());
            sysModel.setTableName(tableInfo.getName());
            sysModel.setIndexName(tableInfo.getIndexName());
            sysModel.setDataSourceDefCode(tableInfo.getDataSourceDefCode());
            sysModel.setClassName(domainType);
            sysModel.setSystemFlag(0);
            sysModel.setBaseAttrVal(userCredentials);

            // 模型引用子表关系
            List<IRelation> hasManyMappingList = tableInfo.getHasManyMappingList();
            hasManyMappingList.forEach(hasManyMapping ->{
                SysModelMany sysModelMany = new SysModelMany();
                sysModelMany.setManyId(IdCreateUtil.getSnowflakeId());
                sysModelMany.setModelId(sysModel.getModelId());
                sysModelMany.setAsName(hasManyMapping.getAs());
                sysModelMany.setFKey(hasManyMapping.getForeignKey());
                sysModelMany.setTarKey(hasManyMapping.getTargetKey());
                sysModelMany.setClassName(hasManyMapping.getTypeName());
                sysModelMany.setTarModelCode(hasManyMapping.getTypeName());
                sysModelMany.setRefStr(hasManyMapping.getRefTableAddString());
                //  tarModelId 获取不到 暂时写死为1000 TODO
                sysModelMany.setTarModelId(1000L);
                sysModelMany.setBaseAttrVal(userCredentials);
                System.out.println(sysModelMany.getDataType());
                // 缺少模型目标模型ID tarModelId
                modelManyList.add(sysModelMany);
            });
            // 模型引用关系
            List<IRelation> belongsToMappingList = tableInfo.getBelongsToMappingList();
            belongsToMappingList.forEach(hasManyMapping ->{
                SysModelBelong sysModelBelong = new SysModelBelong();
                sysModelBelong.setBelongId(IdCreateUtil.getSnowflakeId());
                sysModelBelong.setModelId(sysModel.getModelId());
                sysModelBelong.setAsName(hasManyMapping.getAs());
                sysModelBelong.setFKey(hasManyMapping.getForeignKey());
                sysModelBelong.setTarKey(hasManyMapping.getTargetKey());
                sysModelBelong.setClassName(hasManyMapping.getTypeName());
                sysModelBelong.setTarModelCode(hasManyMapping.getTypeName());
                sysModelBelong.setRefStr(hasManyMapping.getRefTableAddString());

                sysModelBelong.setTarModelId(1000L);
                sysModelBelong.setBaseAttrVal(userCredentials);
                sysModelBelongList.add(sysModelBelong);
            });

            // 模型字段列表
            List<IColumnMapping> columnMappingList = tableInfo.getColumnMappingList();
            Integer indexSortNo = 0;
            for(IColumnMapping columnMapping:columnMappingList){
                indexSortNo++;
                SysModelAttr sysModelAttr = new SysModelAttr();
                sysModelAttr.setAttrId(IdCreateUtil.getSnowflakeId());
                sysModelAttr.setModelId(sysModel.getModelId());
                sysModelAttr.setAttrName(columnMapping.getFieldName());
                sysModelAttr.setType(columnMapping.getDataType().getName());
                // ATTR_DESC 为空则使用数据库字段
                sysModelAttr.setDesc(StringUtil.isEmpty(columnMapping.getDesc())? columnMapping.getName() : columnMapping.getDesc());
                // code 默认类型
                sysModelAttr.setCode(columnMapping.getName());
                //no 值为字段的顺序
                sysModelAttr.setSortNo(indexSortNo);
                sysModelAttr.setLen(columnMapping.getLength());
                sysModelAttr.setSacle(columnMapping.getScale());

                sysModelAttr.setPkFlag(columnMapping.getPrimaryKeyFlag()?"Y":"N");
                sysModelAttr.setAkFlag(columnMapping.getUniqueFlag()?"Y":"N");
                sysModelAttr.setReadonlyFlag(columnMapping.getReadonly()?"Y":"N");
                sysModelAttr.setUseDbFlag(columnMapping.getUseDbObjectFlag()?"Y":"N");
                sysModelAttr.setDbExpress(columnMapping.getDbExpression());
                sysModelAttr.setVerFlagValue(columnMapping.getVersionFlag()?"Y":"N");
                sysModelAttr.setDelFlagValue(columnMapping.getDeleteFlag()?"Y":"N");
                sysModelAttr.setRequiredFlag(!columnMapping.getNullFlag()?"Y":"N");

                if((StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"CREATEBY"))||(StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"CREATETIME"))||
                        (StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"UPDATEBY"))||(StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"UPDATETIME"))||
                        (StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"VERSION"))||(StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"DELFLAG"))||
                        (StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"CREATEDEPT"))||(StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"UPDATEDEPT"))
                        ||(StringUtil.equals(columnMapping.getFieldName().toUpperCase(),"UPDATEIP"))){
                    sysModelAttr.setBuildType(1);
                }else{
                    sysModelAttr.setBuildType(2);
                }

                // 引用的字段处理器
                columnMapping.getAutoFillBeanList().forEach(iAutoFillBean -> {
                    SysModelAttrValProc sysModelAttrValProc = new SysModelAttrValProc();
                    sysModelAttrValProc.setValprocId(IdCreateUtil.getSnowflakeId());
                    sysModelAttrValProc.setModelId(sysModelAttr.getModelId());
                    sysModelAttrValProc.setAttrId(sysModelAttr.getAttrId());
                    sysModelAttrValProc.setAutoType(iAutoFillBean.getAutoFillType().name());
                    // 自动填充参数
                    sysModelAttrValProc.setArgs(StringUtil.isEmpty(iAutoFillBean.getArgs())? "" : iAutoFillBean.getArgs());
                    // 填充默认值
                    sysModelAttrValProc.setDefVal(StringUtil.isEmpty(iAutoFillBean.getDefVal())? "" : iAutoFillBean.getDefVal());
                    // 自定义填充类型
                    sysModelAttrValProc.setCustomType(StringUtil.isEmpty(iAutoFillBean.getType())? "" : iAutoFillBean.getType());
                    sysModelAttrValProc.setOverwriteFlag(iAutoFillBean.getOverwrite()? 1 : 0);
                    sysModelAttrValProc.setOpMark(iAutoFillBean.getOperateMark());
                    // 排序
                    sysModelAttrValProc.setSortNo(iAutoFillBean.getOrder());
                    sysModelAttrValProc.setBaseAttrVal(userCredentials);
                    sysModelAttrValProcList.add(sysModelAttrValProc);
                });

                sysModelAttr.setBaseAttrVal(userCredentials);
                modelAttrList.add(sysModelAttr);
            }

            sysModel.modelBelongList = sysModelBelongList;
            sysModel.modelManyList = modelManyList;
            sysModel.attrValProcList = sysModelAttrValProcList;
            sysModel.modelAttrList = modelAttrList;
            Collections.sort(sysModel.modelAttrList);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return sysModel;
    }

    /**
     * 获取完整的SysModel对象
     * @param dataSourceName 数据源名称
     * @param modelId 模型ID
     *
     * @return SysModel对象
     */
    public static SysModel getInstance(String dataSourceName,Long modelId){
        return getInstance(DataBaseAccess.getInstanceByName(dataSourceName),modelId);
    }

    /**
     * 获取完整的SysModel对象
     * @param modelId 模型ID
     *
     * @return SysModel对象
     */
    public static SysModel getInstance(Long modelId){

        return getInstance(DataBaseAccess.getInstance(),modelId);
    }

    /**
     * 获取完整的SysModel对象
     * @param modelCode 模型编码
     *
     * @return SysModel对象
     */
    public static SysModel getInstance(String modelCode){
        return getInstance(DataBaseAccess.getInstance(),modelCode);
    }

    /**
     * 获取完整的SysModel对象
     * @param modelId 模型ID
     *
     * @return SysModel对象
     */
    public static SysModel getInstance(Long modelId,String modelCode){
        if(modelId != null) {
            return getInstance(DataBaseAccess.getInstance(), modelId);
        }else {
            return getInstance(DataBaseAccess.getInstance(), modelCode);
        }
    }
    /**
     * 获取完整的SysModel对象
     * @param dataBaseAccess 数据源对象
     * @param modelId 模型ID
     *
     * @return SysModel对象
     */
    public static SysModel getInstance(DataBaseAccess dataBaseAccess,Long modelId){
        return dataBaseAccess.findByPk(SysModel.class,modelId, SysBaseEntity.FULL_DATA);
    }

    public static SysModel getInstance(DataBaseAccess dataBaseAccess,String modelCode){
        return dataBaseAccess.findByAk(AKQueryModel.valueOf(SysModel.class,modelCode, SysBaseEntity.FULL_DATA));
    }


    /**
     * 完整SysModel对象检查
     * 1.字段列表检查(必须不能为空)
     * 2.必须包含PK  AK  如没有AK  则PK = AK
     * 3.如果有引用列则检查
     */
    public void check() throws Exception{


    }

    @Override
    public void build() {

    }

}
